﻿'This file is part of Alpha.
'
'   Alpha is free software: you can redistribute it and/or modify
'   it under the terms of the GNU Lesser General Public License as published by
'   the Free Software Foundation, either version 3 of the License, or
'   (at your option) any later version.
'
'   Alpha is distributed in the hope that it will be useful,
'   but WITHOUT ANY WARRANTY; without even the implied warranty of
'   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
'   GNU Lesser General Public License for more details.
'
'   You should have received a copy of the GNU Lesser General Public License
'   along with Alpha.  If not, see <http://www.gnu.org/licenses/>.

Namespace Controllers
    Public Class Winamp
        Inherits Controller

#Region " Win32 Functions "

        

        Private Overloads Function SendCommand(ByVal command As Integer, Optional ByVal type As Boolean = False, Optional ByVal parameter As Integer = 0) As Integer
            Dim newparameter As IntPtr = New IntPtr(parameter)
            If type Then
                Return NativeMethods.SendMessage(hWnd, 1024, newparameter, New IntPtr(command)).ToInt32
            Else
                Return NativeMethods.SendMessage(hWnd, 273, New IntPtr(command), newparameter).ToInt32
            End If
        End Function

        'Private Overloads Function SendCommand(ByVal command As Integer, ByVal parameter As String) As String
        'Dim sarr As Byte() = System.Text.Encoding.Default.GetBytes(parameter)
        'Dim cds As COPYDATASTRUCT
        '    cds.dwData = New IntPtr(dwData)
        '    cds.lpData = parameter
        '    cds.cbData = sarr.Length + 1
        '    SendMessage(hWnd, 74, command, ref cds);
        'End Function

        'Private Structure COPYDATASTRUCT
        'Public dwData As IntPtr
        'Public cbData As Integer
        '[MarshalAs(UnmanagedType.LPStr)]
        'Public lpData As String
        'End Structure




#End Region

#Region " Properties "

        Dim WithEvents timer As System.Timers.Timer
        Dim oldtrack As String = ""
        Dim oldlength As Integer = 0
        Dim hWndhandle As IntPtr = IntPtr.Zero

#End Region

        Public Sub New()
            oldtrack = TrackTitle
            oldlength = TrackLength
            timer = New System.Timers.Timer(1000)
            timer.Start()
        End Sub

#Region " Controller Functions "

        Public Overrides ReadOnly Property SupportedActions() As Collections.ObjectModel.ReadOnlyCollection(Of Action)
            Get
                Dim list As Collections.Generic.List(Of Action) = New Collections.Generic.List(Of Action)
                list.Add(Action.Play)
                list.Add(Action.Pause)
                list.Add(Action.StopPlayback)
                list.Add(Action.EventTrackChanged)
                Return New Collections.ObjectModel.ReadOnlyCollection(Of Action)(list)
            End Get
        End Property

        Public Overrides ReadOnly Property Version() As String
            Get
                Return "Winamp Controller version 0.2"
            End Get
        End Property

        Public Overrides ReadOnly Property Copyright() As String
            Get
                Return "Copyright Matthew Wilkinson 2008-2009. Based in part on the work of David Stein."
            End Get
        End Property

#End Region

#Region " Public Stuff "

        Public Overrides Sub Play()
            If PlayerState = State.Playing Then
                Throw New AlreadyPlayingException
            Else
                SendCommand(Commands.Play)
            End If
        End Sub

        Public Overrides Sub Pause()
            Dim state As State = PlayerState
            If state = state.Paused Then
                Throw New AlreadyPausedException
            ElseIf state = state.Stopped Then
                Throw New NotPlayingException
            Else
                SendCommand(Commands.Pause)
            End If
        End Sub

        Public Overrides Sub PlayPause()
            Dim state As State = PlayerState
            If state = state.Paused Or state = state.Stopped Then
                SendCommand(Commands.Play)
            Else
                SendCommand(Commands.Pause)
            End If
        End Sub

        Public Overrides Sub StopPlayback()
            If PlayerState = State.Stopped Then
                Throw New NotPlayingException
            Else
                SendCommand(Commands.StopPlayback)
            End If
        End Sub

        Public Overrides Sub NextTrack()
            Dim state As State = PlayerState
            If state = state.Stopped Or state = state.Paused Then
                Throw New NotPlayingException
            Else
                SendCommand(Commands.NextTrack)
            End If
        End Sub

        Public Overrides Sub PreviousTrack()
            Dim state As State = PlayerState
            If state = state.Stopped Or state = state.Paused Then
                Throw New NotPlayingException
            Else
                SendCommand(Commands.PrevTrack)
            End If
        End Sub

        Public Overrides Property Volume() As Integer
            Get
                Dim wvol As Integer = SendCommand(Commands.SetVolume, True, -666)
                Return CType(Math.Round(wvol / 255 * 100, 0), Integer)
            End Get
            Set(ByVal volume As Integer)
                If volume < 100 And volume > -1 Then
                    SendCommand(Commands.SetVolume, True, CType(volume / 100 * 255, Integer))
                End If
            End Set
        End Property

        Public Overrides ReadOnly Property Track() As Track
            Get
                Return New Track With {.Length = TrackLength}
            End Get
        End Property

        Public Overrides ReadOnly Property TimeElapsed() As Integer
            Get
                Return CType(SendCommand(Commands.TrackLength, True) / 1000, Integer)
            End Get
        End Property




#End Region

#Region " Winamp Internals "

        Private Enum Commands As Integer
            PrevTrack = 40044
            Play = 40045
            Pause = 40046
            StopPlayback = 40047
            NextTrack = 40048
            VolumeUp = 40058
            VolumeDown = 40059
            IsPlaying = 104
            TrackLength = 105 ' Must also pass 0 for current song time, or 1 for current song length
            Seek = 106
            SetVolume = 122
            GetShuffle = 250
            GetRepeat = 251
            SetShuffle = 252
            SetRepeat = 253
        End Enum

        Private ReadOnly Property PlayerState() As State
            Get
                Select Case SendCommand(Commands.IsPlaying, True)
                    Case 0
                        Return State.Stopped
                    Case 1
                        Return State.Playing
                    Case 3
                        Return State.Paused
                End Select
            End Get
        End Property

        Private ReadOnly Property TrackLength() As Integer
            Get
                Return SendCommand(Commands.TrackLength, True, 1)
            End Get
        End Property

        'Technically no reliable way to get Track Title as user can specify how they want the title bar to be formatted
        'But we can still try to use it to determine if the track has changed - in conjuction with the current track length
        Private ReadOnly Property TrackTitle() As String
            Get
                Dim length As Integer = NativeMethods.GetWindowTextLength(hWnd)
                If length = 0 Then
                    Return ""
                End If
                Dim sb As New System.Text.StringBuilder("", length + 1)
                Dim returnval As Integer = NativeMethods.GetWindowText(hWnd, sb, sb.Capacity)
                returnval = Nothing
                Dim windowtext As String = sb.ToString
                Dim lastindex As Integer = windowtext.LastIndexOf(" - Winamp", StringComparison.Ordinal)
                Return windowtext.Substring(windowtext.IndexOf(". ", StringComparison.Ordinal) + 1, windowtext.LastIndexOf(" - Winamp", StringComparison.Ordinal) - 2)
            End Get
        End Property

        Private ReadOnly Property hWnd() As IntPtr
            Get
                hWndhandle = NativeMethods.FindWindow("Winamp v1.x", "")
                If hWndhandle.ToInt32 = 0 Then
                    Throw New PlayerNotRunningException
                Else
                    Return hWndhandle
                End If
            End Get
        End Property

        Private Enum State
            Playing
            Paused
            Stopped
        End Enum



#End Region



#Region " Event Support "

        Private Sub Update() Handles timer.Elapsed
            Dim title As String = TrackTitle
            Dim length As Integer = TrackLength
            If title <> oldtrack And length <> oldlength Then
                oldtrack = title
                oldlength = length
                RaiseEvent TrackChanged()
            End If
        End Sub

        Public Shadows Event TrackChanged()

#End Region

#Region " IDisposable Support "
        Private disposedValue As Boolean

        ' IDisposable
        Protected Overrides Sub Dispose(ByVal disposing As Boolean)
            If Not Me.disposedValue Then
                If disposing Then
                    timer.Dispose()
                    timer = Nothing
                End If
            End If
            Me.disposedValue = True
        End Sub

#End Region

    End Class
End Namespace
